#pragma once

#include <iostream>
#include <list>

namespace visitor {

/// @brief 抽象访问者, 定义一个访问具体元素的接口，为每个具体元素类对应一个访问操作，该操作中的参数类型标识了被访问的具体元素。
class ConcreteElementA;
class ConcreteElementB;
class AbstractVisitor {
public:
    virtual ~AbstractVisitor() {}

    /// @brief 访问操作, 依赖于具体元素类
    virtual void Visit(ConcreteElementA* element) = 0;
    virtual void Visit(ConcreteElementB* element) = 0;
};

/// @brief 具体访问者, 实现抽象访问者角色中声明的各个访问操作，确定访问者访问一个元素时该做什么。
class ConcreteVisitorA : public AbstractVisitor {
public:
    virtual void Visit(ConcreteElementA* element);
    virtual void Visit(ConcreteElementB* element);
};
class ConcreteVisitorB : public AbstractVisitor {
public:
    virtual void Visit(ConcreteElementA* element);
    virtual void Visit(ConcreteElementB* element);
};

/// @brief 抽象元素, 声明一个包含接受操作 accept() 的接口，被接受的访问者对象作为 accept() 方法的参数
class AbstractElement {
public:
    virtual ~AbstractElement() {}

    virtual void Accept(AbstractVisitor* visitor) = 0;
};

/// @brief 具体元素角色：实现抽象元素角色提供的 accept() 操作，另外具体元素中可能还包含本身业务逻辑的相关操作。
class ConcreteElementA : public AbstractElement {
public:
    virtual void Accept(AbstractVisitor* visitor) {
        visitor->Visit(this);
    }
    
    void OperatorA() {
        printf("this is ConcreteElementA::OperatorA!\n");
    }
};
class ConcreteElementB : public AbstractElement {
public:
    virtual void Accept(AbstractVisitor* visitor) {
        visitor->Visit(this);
    }
    
    void OperatorB() {
        printf("this is ConcreteElementB::OperatorB!\n");
    }
};

void ConcreteVisitorA::Visit(ConcreteElementA* element) {
    printf("this is ConcreteVisitorA::Visit!\n");
    element->OperatorA();
}
void ConcreteVisitorA::Visit(ConcreteElementB* element) {
    printf("this is ConcreteVisitorA::Visit!\n");
    element->OperatorB();
}
void ConcreteVisitorB::Visit(ConcreteElementA* element) {
    printf("this is ConcreteVisitorB::Visit!\n");
    element->OperatorA();
}
void ConcreteVisitorB::Visit(ConcreteElementB* element) {
    printf("this is ConcreteVisitorB::Visit!\n");
    element->OperatorB();
}

} // namespace visitor

void Test_21_visitor_impl_1() {
    printf("-------------------- %s --------------------\n", __FUNCTION__);
    std::list<visitor::AbstractElement*> elements;
    elements.emplace_back(new visitor::ConcreteElementA());
    elements.emplace_back(new visitor::ConcreteElementB());

    // 为所有元素设置访问者A
    std::shared_ptr<visitor::AbstractVisitor> ptr_visitor_a(new visitor::ConcreteVisitorA());
    for (auto* e : elements) {
        e->Accept(ptr_visitor_a.get());
    }

    printf("----------------------\n");

    // 为所有元素设置访问者B
    std::shared_ptr<visitor::AbstractVisitor> ptr_visitor_b(new visitor::ConcreteVisitorB());
    for (auto* e : elements) {
        e->Accept(ptr_visitor_b.get());
    }

    for (auto* e : elements) {
        if (e) delete e;
    }
}

// -------------------- Test_20_visitor_impl_1 --------------------
// this is ConcreteVisitorA::Visit!
// this is ConcreteElementA::OperatorA!
// this is ConcreteVisitorA::Visit!
// this is ConcreteElementB::OperatorB!
// ----------------------
// this is ConcreteVisitorB::Visit!
// this is ConcreteElementA::OperatorA!
// this is ConcreteVisitorB::Visit!
// this is ConcreteElementB::OperatorB!